package com.xlh.support.quiz.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.xlh.aop.PreAcquireLock;
import com.xlh.dto.courseTest.experiment.TheoryChapterStatisticsExportDTO;
import com.xlh.dto.user.UserInfoDTO;
import com.xlh.exception.common.GlobalException;
import com.xlh.pojo.user.User;
import com.xlh.service.course.ChapterService;
import com.xlh.service.user.UserService;
import com.xlh.support.attendance.service.AttendanceService;
import com.xlh.support.quiz.dao.QuizAnswerMapper;
import com.xlh.support.quiz.dao.QuizMapper;
import com.xlh.support.quiz.dao.ext.QuizMapperExt;
import com.xlh.support.quiz.dao.helper.QuizAnswerMapperHelper;
import com.xlh.support.quiz.dao.helper.QuizMapperHelper;
import com.xlh.support.quiz.domain.Quiz;
import com.xlh.support.quiz.domain.QuizAnswer;
import com.xlh.support.quiz.po.QuizAnswerPO;
import com.xlh.support.quiz.po.QuizAssessPO;
import com.xlh.support.quiz.service.QuizService;
import com.xlh.support.quiz.vo.PerformanceVO;
import com.xlh.support.quiz.vo.UserPerformanceCountVO;
import com.xlh.support.quiz.websocket.message.QuizMessage;
import com.xlh.websocket.GlobalWebsocket;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.xlh.support.quiz.constant.QuizConstant.*;
import static com.xlh.support.quiz.constant.QuizPerformanceEnum.*;

/**
 * @author cheer
 */
@Service
public class QuizServiceImpl implements QuizService {

    @Autowired
    private ChapterService chapterService;

    @Autowired
    private UserService userService;

    @Autowired
    private AttendanceService attendanceService;

    @Autowired
    private QuizMapper quizMapper;

    @Autowired
    private QuizMapperExt quizMapperExt;

    @Autowired
    private QuizMapperHelper quizMapperHelper;

    @Autowired
    private QuizAnswerMapper quizAnswerMapper;

    @Autowired
    private QuizAnswerMapperHelper quizAnswerMapperHelper;

    /**
     * 进行中的随堂提问
     */
    private Map<Long, Integer> runningQuizMap = new ConcurrentHashMap<>();

    /**
     * 进行中的提问章节
     */
    private Set<Long> runningChapterSet = Sets.newConcurrentHashSet();


    @Override
    @PreAcquireLock(value = QUIZ_ASK_KEY)
    public void askQuiz(Long quizId) {
        Quiz quiz = quizMapperHelper.getById(quizId);
        if (quiz == null) {
            throw new GlobalException("题目不存在");
        }

        List<UserInfoDTO> students = chapterService.listStudent(quiz.getChapterId());
        if (CollectionUtils.isEmpty(students)) {
            throw new GlobalException("学生不存在");
        }

        if (runningChapterSet.contains(quiz.getChapterId())) {
            throw new GlobalException("有提问正在进行中，结束该提问才能继续下一个提问");
        }

        int currentQuizNumber = quiz.getQuizNumber() + 1;
        quizMapperHelper.updateQuizNumber(quizId, currentQuizNumber);

        runningQuizMap.put(quizId, currentQuizNumber);
        runningChapterSet.add(quiz.getChapterId());

        // 通过websocket把随堂提问发送给学生
        List<Long> studentIds = students.stream().map(UserInfoDTO::getId).collect(Collectors.toList());
        QuizMessage quizMessage = QuizMessage.builder()
                .quizId(quizId)
                .quizName(quiz.getQuizName())
                .quizNumber(currentQuizNumber)
                .build();
        GlobalWebsocket.sendMessage(studentIds, quizMessage);
    }

    @Override
    @PreAcquireLock(value = QUIZ_ANSWER_KEY)
    public void answerQuiz(Long userId, Long quizId, QuizAnswerPO quizAnswerPO) {
        if (!runningQuizMap.containsKey(quizId) ||
                !Objects.equals(runningQuizMap.get(quizId), quizAnswerPO.getQuizNumber())) {
            throw new GlobalException("随堂提问已结束");
        }

        Quiz quiz = quizMapperHelper.getById(quizId);
        if (quiz == null) {
            throw new GlobalException("题目不存在");
        }

        if (quizAnswerMapperHelper.getByUserIdAndQuizId(userId, quizId, quizAnswerPO.getQuizNumber()) != null) {
            throw new GlobalException("不能重复提交答案");
        }

        QuizAnswer answer = QuizAnswer.builder()
                .userId(userId)
                .quizId(quizId)
                .quizNumber(quizAnswerPO.getQuizNumber())
                .performance(quizAnswerPO.getAnswer() == quiz.getJudgementAnswer() ?
                        CORRECT.getCode() : INCORRECT.getCode())
                .build();
        quizAnswerMapper.insertSelective(answer);
    }

    @Override
    public PerformanceVO endQuiz(Long quizId) {
        if (!runningQuizMap.containsKey(quizId)) {
            throw new GlobalException("随堂提问不存在");
        }

        Quiz quiz = quizMapper.selectByPrimaryKey(quizId);
        if (quiz == null || quiz.getDeleted()) {
            throw new GlobalException("题目不存在");
        }

        int quizNumber = runningQuizMap.remove(quizId);
        runningChapterSet.remove(quiz.getChapterId());

        List<QuizAnswer> answers = quizAnswerMapperHelper.listByQuizIdAndNumber(quizId, quizNumber);
        if (CollectionUtils.isEmpty(answers)) {
            return PerformanceVO.builder()
                    .totalCount(0)
                    .correctCount(0)
                    .incorrectCount(0)
                    .correctPercent("0%")
                    .correctStudents(new ArrayList<>())
                    .incorrectStudents(new ArrayList<>())
                    .build();
        }

        Map<Long, User> userMap = listUsersByAnswers(answers);

        int totalCount = 0;
        int correctCount = 0;
        int incorrectCount = 0;
        List<String> correctStudentNameList = new ArrayList<>();
        List<String> incorrectStudentNameList = new ArrayList<>();

        for (QuizAnswer answer : answers) {
            if (userMap.containsKey(answer.getUserId())) {
                totalCount++;
                String username = userMap.get(answer.getUserId()).getName();
                if (Objects.equals(answer.getPerformance(), CORRECT.getCode())) {
                    correctCount++;
                    correctStudentNameList.add(username);
                } else if (Objects.equals(answer.getPerformance(), INCORRECT.getCode())) {
                    incorrectCount++;
                    incorrectStudentNameList.add(username);
                }
            }
        }

        String correctPercent = totalCount > 0 ?
                new DecimalFormat("0%").format((double) correctCount / totalCount) : "0%";
        return PerformanceVO.builder()
                .totalCount(totalCount)
                .correctCount(correctCount)
                .incorrectCount(incorrectCount)
                .correctPercent(correctPercent)
                .correctStudents(correctStudentNameList)
                .incorrectStudents(incorrectStudentNameList)
                .build();
    }

    @Override
    @Transactional
    @PreAcquireLock(value = QUIZ_ASSESS_KEY)
    public void assessQuiz(Long quizId, QuizAssessPO quizAssessPO) {
        int performance = quizAssessPO.getPerformance();
        if (performance != EXCELLENT.getCode() && performance != GOOD.getCode() &&
                performance != POOR.getCode() && performance != ABSENT.getCode()) {
            throw new GlobalException("评价选项不是合法的值");
        }

        Quiz quiz = quizMapperHelper.getById(quizId);
        if (quiz == null) {
            throw new GlobalException("题目不存在");
        }

        int currentQuizNumber = quiz.getQuizNumber() + 1;
        quizMapperHelper.updateQuizNumber(quizId, currentQuizNumber);

        if (performance == ABSENT.getCode()) {
            attendanceService.addAbsent(quizAssessPO.getCourseId(), quizAssessPO.getClassId(), quizAssessPO.getUserId());
        } else {
            QuizAnswer answer = QuizAnswer.builder()
                    .userId(quizAssessPO.getUserId())
                    .quizId(quizId)
                    .quizNumber(currentQuizNumber)
                    .performance(quizAssessPO.getPerformance())
                    .build();
            quizAnswerMapper.insertSelective(answer);
        }
    }

    @Override
    public Boolean isQuizRunning(Long quizId) {
        return runningQuizMap.containsKey(quizId);
    }

    @Override
    public void deleteRunningQuiz(List<Long> quizIds, List<Long> chapterIds) {
        if (CollectionUtils.isNotEmpty(quizIds)) {
            for (Long quizId : quizIds) {
                runningQuizMap.remove(quizId);
            }
        }

        if (CollectionUtils.isNotEmpty(chapterIds)) {
            for (Long chapterId : chapterIds) {
                runningChapterSet.remove(chapterId);
            }
        }
    }

    @Override
    public List<TheoryChapterStatisticsExportDTO> getUserPerformanceByChapterId(Long chapterId, List<Long> userIds) {

        List<TheoryChapterStatisticsExportDTO> mergeResult = Lists.newArrayList();

        Map<Long, Long> correntList = getUserPerformance(chapterId, userIds, CORRECT.getCode());
        Map<Long, Long> inCorrentList = getUserPerformance(chapterId, userIds, INCORRECT.getCode());
        Map<Long, Long> excellentList = getUserPerformance(chapterId, userIds, EXCELLENT.getCode());
        Map<Long, Long> goodList = getUserPerformance(chapterId, userIds, GOOD.getCode());
        Map<Long, Long> poorList = getUserPerformance(chapterId, userIds, POOR.getCode());
        Function<Long, Long> function = x-> { if(ObjectUtil.isNull(x)){ return 0L;}return x; };
        // 合并所有的list
        for (Long userId : userIds) {
            TheoryChapterStatisticsExportDTO theoryChaper = new TheoryChapterStatisticsExportDTO();

            theoryChaper.setUserId(userId);
            theoryChaper.setCorrect(function.apply(correntList.get(userId)));
            theoryChaper.setInCorrect(function.apply(inCorrentList.get(userId)));
            theoryChaper.setExcellent(function.apply(excellentList.get(userId)));
            theoryChaper.setGood(function.apply(goodList.get(userId)));
            theoryChaper.setPoor(function.apply(poorList.get(userId)));
            mergeResult.add(theoryChaper);
        }
        return  mergeResult;
    }

    private Map<Long, Long> getUserPerformance(Long chapterId, List<Long> userIds, Integer code) {

        List<UserPerformanceCountVO> userPerformance = quizMapperExt.getUserPerformance(chapterId, userIds, code);
        Map<Long, List<UserPerformanceCountVO>> userIdMap = userPerformance.stream().collect(Collectors.groupingBy(UserPerformanceCountVO::getUserId));
        Map<Long, Long> userPerformanceResult = new HashMap<>();
        for (Long userId : userIdMap.keySet()) {
            UserPerformanceCountVO longLongMap = userIdMap.get(userId).get(0);
            userPerformanceResult.put( userId, longLongMap.getCount());
        }
        for (Long userId : userIds) {
            if(ObjectUtil.isNull(userPerformanceResult.get(userId))){
                userPerformanceResult.put(userId,0L);
            }
        }
        return userPerformanceResult;
    }

    private Map<Long, User> listUsersByAnswers(List<QuizAnswer> answers) {
        List<Long> userIds = answers.stream().map(QuizAnswer::getUserId).collect(Collectors.toList());
        List<User> users = userService.listUser(userIds);
        Map<Long, User> userMap = new HashMap<>();
        for (User user : users) {
            userMap.put(user.getId(), user);
        }
        return userMap;
    }
}
